package com.zhejiangzhengyuan.municipal_common.response;

import com.zhejiangzhengyuan.municipal_common.entity.ExceptionLog;
import com.zhejiangzhengyuan.municipal_common.mapper.ExceptionLogMapper;
import com.zhejiangzhengyuan.municipal_common.mapper.SubjectMapper;
import com.zhejiangzhengyuan.municipal_common.utils.*;
import com.zhejiangzhengyuan.municipal_common.utils.httpUtils.*;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.zhejiangzhengyuan.municipal_common.utils.JwtUtil.HEADER_STRING;

/**
 * Controller 统一返回包装
 *
 * @author lin_ju
 * @since  on 2019/11/22
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class ResultResponseBodyAdvice {
  @Autowired SubjectMapper subjectMapper;
  @Autowired  ExceptionLogMapper exceptionLogMapper;
  /**
   * Controller body参数校验异常处理
   *
   * @param exception body校验异常信息类
   * @return Response封装实体类
   */
  @ExceptionHandler(value = MethodArgumentNotValidException.class)
  public ResponseVo<Object> MethodArgumentNotValidHandler(MethodArgumentNotValidException exception)
      throws Exception {
    // 按需重新封装需要返回的错误信息
    List<InvalidResult> invalidArguments = new ArrayList<>();
    // 解析原错误信息，封装后返回，此处返回非法的字段名称，原始值，错误信息
    for (FieldError error : exception.getBindingResult().getFieldErrors()) {
      InvalidResult invalidArgument = new InvalidResult();
      invalidArgument.setDefaultMessage(error.getDefaultMessage());
      invalidArgument.setField(error.getField());
      invalidArgument.setRejectedValue(error.getRejectedValue());
      invalidArguments.add(invalidArgument);
    }
    return new ResponseVo<>(ConstantUtil.ResultCode.CLIENT_PARAM_ERROR, invalidArguments);
  }

  /**
   * 方法内参数校验异常处理
   *
   * @param ex 单个参数校验异常信息类
   * @return Response封装实体类
   */
  @ExceptionHandler(value = ConstraintViolationException.class)
  public ResponseVo<Object> handleMethodArgumentNotValidException(ConstraintViolationException ex) {
    Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
    List<InvalidResult> invalidArguments = new ArrayList<>();
    // 封装异常返回信息
    constraintViolations.forEach(
        constraintViolation -> {
          PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath();
          // 读取参数字段，constraintViolation.getMessage() 读取验证注解中的message值
          InvalidResult invalidArgument = new InvalidResult();
          invalidArgument.setField(pathImpl.getLeafNode().getName());
          invalidArgument.setDefaultMessage(constraintViolation.getMessage());
          invalidArguments.add(invalidArgument);
        });
    return new ResponseVo<>(ConstantUtil.ResultCode.CLIENT_PARAM_ERROR, invalidArguments);
  }

  /**
   * 方法内参数校验异常处理
   *
   * @param ex formData参数异常信息类
   * @return Response封装实体类
   */
  @ExceptionHandler(value = BindException.class)
  public ResponseVo<Object> handleMethodArgumentNotValidException(BindException ex) {
    List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
    List<InvalidResult> invalidArguments = new ArrayList<>();
    // 封装异常返回信息
    fieldErrors.forEach(
        fieldError -> {
          InvalidResult invalidArgument = new InvalidResult();
          invalidArgument.setField(fieldError.getField());
          invalidArgument.setDefaultMessage(fieldError.getDefaultMessage());
          invalidArguments.add(invalidArgument);
        });
    return new ResponseVo<>(ConstantUtil.ResultCode.CLIENT_PARAM_ERROR, invalidArguments);
  }

  // 需要携带token缺未携带
  @ExceptionHandler(value = UnauthorizedException.class)
  public ResponseVo handleMethodArgumentNotValidException(UnauthorizedException ex) {
    return ResponseVo.UN_AUTHORIZED;
  }

  // 权限被拒绝
  @ExceptionHandler(value = ForbiddenException.class)
  public ResponseVo handleMethodArgumentNotValidException(ForbiddenException ex) {
    return ResponseVo.FORBIDDEN;
  }

  // 页面未找到会在jwt鉴权中抛出该异常
  @ExceptionHandler(value = NotFoundException.class)
  public ResponseVo handleMethodArgumentNotValidException(NotFoundException ex) {
    return ResponseVo.NOT_FOUND_PATH;
  }

  // 页面未找到会在jwt鉴权中抛出该异常
  @ExceptionHandler(value = ServerErrorException.class)
  public ResponseVo handleMethodArgumentNotValidException(ServerErrorException ex) {
    return ResponseVo.SERVER_ERROR;
  }

  // request存放位置错误
  @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
  public ResponseVo handleMethodArgumentNotValidException(HttpMediaTypeNotSupportedException ex) {
    return new ResponseVo(ConstantUtil.HTTP_CODE.CLIENT_PARAM_ERROR,"参数请放在ROW_JSON内(JSONP)");
  }
  //i batis数据库错误
  @ExceptionHandler(value = PersistenceException.class)
    public ResponseVo handleMethodArgumentNotValidException(PersistenceException ex){
      return ResponseVo.errorWithException(ex);

  }

  //添加异常记录
  @ExceptionHandler(value = Exception.class)
  public void errorHandler(HttpServletRequest request,Exception ex) throws Exception{
    ExceptionLog log=new ExceptionLog();
    StackTraceElement stackTraceElement= ex.getStackTrace()[0];
    log.setType(stackTraceElement.getClassName());
    log.setPosition(String.valueOf(stackTraceElement.getLineNumber()));
    log.setMessage(ex.getLocalizedMessage());
    String token = request.getHeader(HEADER_STRING);
    Claims claims = JwtUtil.parseJWT(token);
    Long id = Long.valueOf(String.valueOf(claims.get("id")));
    log.setSubjectName(subjectMapper.selectById(id).getSubjectName());
    log.setUrl(request.getRequestURI());
    log.setLevel("error");
    log.setVersion(1);
    exceptionLogMapper.insert(log);
    throw ex;
  }


}
